package universe;
import database.MSpecialFeature;
import database.MWorld;
import database.MWorldObject;
import extra.Position;
import factory.Constants;
import factory.ConstantsFactory;
import factory.WorldInterfaceFactory;
import interfaces.ActionInterface;
import interfaces.AgentInterface;
import interfaces.WorldInterface;
import interfaces.impl.HumanInteraction;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import objects.SpetialFeature;
import objects.WorldObject;

import org.hibernate.Session;
import org.hibernate.criterion.Restrictions;

import space.Space;
import util.CledaConnector;
import constants.Message;


public class Universe {
	
	private WorldInterface worldI;
	private Space space= null;
	
	private int universeId =constants.Constants.NO_UNIVERSE_ID;
	
	private int height;
	private int width;
	
	private ArrayList<MessageFromClient> messageList= new ArrayList<MessageFromClient>();

	private ArrayList<WorldObject> listObjects;
	private ArrayList<WorldObject> listDisconnectObjects;

	
	private ArrayList<WorldObject> listObjectsToKill;
	private ArrayList<WorldObject> listObjectsToAdd;
	
	private ArrayList<WorldObject> listObjectsToDesactivate;
	
	private WorldObject backgroundObject;
	
	private ArrayList<AgentInterface> listAgent = new ArrayList<AgentInterface>();
	
	BufferedImage b;
	
	public Universe(int width,int height){
		this.height=height;
		this.width=width;
		
		worldI = WorldInterfaceFactory.createNewWorldInterface(ConstantsFactory.WORLD_FIRST, width, height);
		
		listObjects = new ArrayList<WorldObject>();
	
		listObjectsToKill= new ArrayList<WorldObject>();
		listObjectsToAdd = new ArrayList<WorldObject>();
		listObjectsToDesactivate = new ArrayList<WorldObject>();
		listDisconnectObjects = new ArrayList<WorldObject>();
		listAgent= new ArrayList<AgentInterface>();
		
		messageList= new ArrayList<MessageFromClient>();
	}
	
	
	public void CalculateImageIntersection(WorldObject image){
		worldI.calculateCollision(image.getImageObject());
	}


	public void paint(Graphics g) {
		g.clearRect(0, 0, 600, 600);
		

		worldI.paint(g);
		g.drawImage(b, 35, 35, null);
		  
		
	}


	
	public void addObjectCopy(WorldObject object){
		System.out.println("agrego objetos");
		listObjects.add(new WorldObject(object));
	}
	

	public int getHeight() {
		return height;
		
	}

	public int getWidth() {
		return width;
	}

	public int getRealHeight() {
		return worldI.getRealHeigth();
		
	}

	public int getRealWidth() {
		return worldI.getRealHeigth();
	}

	
	public String saveFile(String save) {
		//saving the world's boxes
		save =width+","+height+System.getProperty("line.separator");
		//save =  world.save(save);
			
		//saving the world objects
		
		Iterator it = listObjects.iterator();
		while (it.hasNext()){
			WorldObject object = (WorldObject) it.next();
			save=object.save(save);
		}
		//System.out.println(save);
		
		return save;
	}


	public void loadFile(String name) {
		InputStreamReader isr;
		String line;
		int x,y;
		try {
			System.out.println("Cargo mundo");
			isr = new InputStreamReader( new FileInputStream((name)));
			BufferedReader input = new BufferedReader(isr);
			
			line=input.readLine();
			String [] linesElements =  line.split(",");
			width=Integer.parseInt(linesElements[0]);
			height=Integer.parseInt(linesElements[1]);
			worldI = WorldInterfaceFactory.createNewWorldInterface(ConstantsFactory.WORLD_FIRST, width, height);
			
			WorldObject object= new WorldObject();
			
			while((line=input.readLine())!=null){
				String comment = line.substring(0, 2);
				System.out.println("Soy la linea "+comment);
				if(comment.equals("//")){
					continue;
				}
				
				linesElements = line.split(",");
				
				if(Integer.parseInt(linesElements[0])==Constants.OBJECT_WORLD_ID){
					object = object.load(linesElements, 1);
				
					listObjects.add(object);
					for(WorldObject obj : object.getListObjects()){
						if(obj.isActive()){
							listObjects.add(obj);
						}
					}
				}
				
			}
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
	}
		
	public ArrayList<WorldObject> getListObjects() {
		return listObjects;
	}


	public void setListObjects(ArrayList<WorldObject> listObjects) {
		this.listObjects = listObjects;
	}

	public WorldObject clickObject(int x, int y) {
		
		WorldObject obj=new WorldObject();
		boolean found =false;
		
		for(WorldObject i : listObjects){
			if(x>i.getImageObject().getPosition().getX() &&y>i.getImageObject().getPosition().getY()){
				if(x<(i.getImageObject().getImageToShow().getWidth()+i.getImageObject().getPosition().getX()) && y<(i.getImageObject().getImageToShow().getHeight()+i.getImageObject().getPosition().getY())){
					Graphics graph;
					BufferedImage image = new BufferedImage(i.getImageObject().getImageToShow().getWidth(),i.getImageObject().getImageToShow().getHeight(),BufferedImage.TYPE_INT_RGB);
					graph= image.createGraphics();
					graph.setColor(new Color(237,2,53));
					graph.fillRect(0, 0, i.getImageObject().getImageToShow().getWidth(), i.getImageObject().getImageToShow().getHeight());
					graph.drawImage(i.getImageObject().getImageToShow(), 0, 0, null);
					if(image.getRGB(x-(int)i.getImageObject().getPosition().getX(), y-(int)i.getImageObject().getPosition().getY())!=graph.getColor().getRGB()){
						if(obj.compareTo(i)<0){
							obj = i;
							found=true;
						}
					}
				}
			}
			
		}
		if(!found){
			return null;
			}
		else{
			return obj;
		}
		
	}

	public ArrayList<WorldObject> getObjects(double x, double y, double x1, double y1, int idObjtectToSearch) {
		ArrayList<WorldObject> list = new ArrayList<WorldObject>();
		for(WorldObject obj : listObjects){
			if(obj.getObjectReference()!=idObjtectToSearch){
				continue;
			}
			if(obj.getPosition().getX()>x && obj.getPosition().getY()>y && obj.getPosition().getX()<x1 && obj.getPosition().getY()<y1){
				list.add(obj);
			}
		}
		return list;
	}

	public ArrayList<WorldObject> getObjects(double x, double y, double x1, double y1) {
		ArrayList<WorldObject> list = new ArrayList<WorldObject>();
		for(WorldObject obj : listObjects){
			if(obj.getPosition().getX()>x && obj.getPosition().getY()>y && obj.getPosition().getX()<x1 && obj.getPosition().getY()<y1){
				list.add(obj);
			}
		}
		return list;
	}

	
	private void searchActiveObject(WorldObject obj){
	
		for(WorldObject object : obj.getListObjects()){
			if(object.isActive() && !listObjects.contains(object)){
				object.setPosition(obj.getPosition());
				listObjectsToAdd.add(object);
			}
			searchActiveObject(object);
		}
		
	}
	
	
	public void update(double dTime) {
		freeCollisions(); //TODO change because now is in server and i dont have yet the images
		for(WorldObject obj : listObjects){
			searchActiveObject(obj);
		}
		
		
		
		for(WorldObject obj : listObjects){
			
			WorldObject objectCopy = new WorldObject(obj);
			
			
			for(AgentInterface ag : obj.getListAgents()){
				ag.update(objectCopy, this, dTime);
				//ag.update(objectCopy, this, dTime);
			}
					
			//i update ech inventory or own object from principal object list
			updadeDeepObjects(objectCopy,dTime);
			
			
			
		//	obj.processAgentsToRemove();
			//worldI.calculateCollision(obj.getImageObject());
			
		}
		
		for(AgentInterface ag :listAgent){
			ag.update(null, this, dTime);
		}
//		Session session = CledaConnector.getInstance().getSession();
//	    session.beginTransaction();
	    
		for(WorldObject obj : listObjects){
			processActions(obj,this, null, null);
			if(!obj.isActive()){
				listObjectsToDesactivate.add(obj);
				
			}
		}
//		session.getTransaction().commit();
//		// SE CIERRA
//		session.close();
		
		addListObjects();
		desactivateObjects();
		killListObjects();
		
		
	}
	
	


	private void desactivateObjects() {
		for(WorldObject obj : listObjectsToDesactivate){
			listObjects.remove(obj);
		}
		
	}


	private void processActions(WorldObject object, Universe universe , Session session, MWorldObject father){

		
		
		
		//System.out.println("GENERe ACcion "+object.getObjectReference());
		
		object.processImmediatlyActions(universe);
		
//		List<MWorldObject> listObj = new ArrayList<MWorldObject>();	
//    	listObj =session.createCriteria(MWorldObject.class).add(Restrictions.eq("objectId", object.getID())).list();
//    	
//    	if(listObj.isEmpty() || listObj.size()>1){
//	    	return;
//	    }
//    	MWorldObject objectToUpdate = new MWorldObject(); 
//		//objectToUpdate = listObj.get(0);
//			
//		objectToUpdate.setObjectReference(father);
//		
//		
//		//objectToUpdate.setWorldReference(mWorld);
//		
//		objectToUpdate.setObjectId(object.getID());
//		objectToUpdate.setObjectRefName(object.getObjectReference());
//		objectToUpdate.setOwner(object.getOwner());
//		objectToUpdate.setX((int)object.getPosition().getX());
//		objectToUpdate.setY((int) object.getPosition().getY());
//		if(object.isActive()){
//			objectToUpdate.setActive(Constants.TRUE);
//			objectToUpdate.setActivate(Constants.TRUE);
//		}else{
//			objectToUpdate.setActive(Constants.FALSE);
//			objectToUpdate.setActivate(Constants.FALSE);
//		}
//		
//		for(MSpecialFeature mFeature : objectToUpdate.getSpecialFeatureList()){
//			SpetialFeature fea = object.getFeature(mFeature.getSpecialFeatureId());
//			if (fea== null ){
//				continue;
//			}
//			mFeature.setName(fea.getName());
//			mFeature.setValue(fea.getValue());
//			mFeature.setAditionalInfo(fea.getAditionalInfo());
//			
//		}
//		
//		if(father!=null){
//			objectToUpdate.setObjectReference(father);
//			father.getObjectList().add(objectToUpdate);
//		}
		
		Object[] list =   object.getListObjects().toArray();
		for(int i=0;i<list.length;i++){
			WorldObject obj = (WorldObject) list[i];
			//obj.processImmediatlyActions(universe);
			
			processActions(obj, universe, session, null);
			//processActions(obj, universe, session, objectToUpdate);
		}
		
		
	}

	private void updadeDeepObjects(WorldObject objectCopy, double dTime) {
		for(WorldObject obj : objectCopy.getListObjects()){
			
			for(AgentInterface ag : obj.getListAgents()){
				ag.update(obj, this, dTime);
			}
			obj.processAgentsToRemove();
			
			updadeDeepObjects(obj,dTime);

		}
		
	}


	private void freeCollisions() {
		worldI.freeCollisionsNodes();
		for(WorldObject obj : listObjects){
			worldI.calculateCollision(obj.getImageObject());
		}
		
	}
	private void removeFromAllObjects(WorldObject obj,WorldObject objectToDelete){
		obj.getListObjects().remove(objectToDelete);
		for(WorldObject object : obj.getListObjects()){
			removeFromAllObjects(object, objectToDelete);
		}
		
		
	}
	private void killListObjects() {
		Session session = CledaConnector.getInstance().getSession();
	    session.beginTransaction();
	     for(WorldObject obj : listObjectsToKill){
	    	 System.out.println("REMUVOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO");
	    	//I remove the object from the object list from the universe
	    	listObjectsToDesactivate.remove(obj); 
	    	if(listObjects.contains(obj)){
	    	   listObjects.remove(obj);
	    	}
	    	
	    	//i remove the object from the fathers
	    	for(WorldObject object : obj.getListFather()){
	    		   object.getListObjects().remove(obj);
	    	}
	    	
	    	//i remove the object as a father from their listfathers
	    	for(WorldObject object : obj.getListObjects()){
	    		   object.getListFather().remove(obj);
	    	}
	    	
	    	for(WorldObject object : listObjects){
	    		removeFromAllObjects(object,obj);
	    	}
	    	
	    	
		    
		    List<MWorldObject> list = new ArrayList<MWorldObject>();
		    list =session.createCriteria(MWorldObject.class).add(Restrictions.eq("id", obj.getID())).list();
	    	
		    if(!list.isEmpty()){
		    	session.delete(list.get(0));
		    }
		    
		    
	    	
	     }
		listObjectsToKill.clear();
		session.getTransaction().commit();
	    // SE CIERRA
	    session.close();
	}


	private void addListObjects() {
			for(WorldObject obj : listObjectsToAdd){
				if(!listObjects.contains(obj)){
					listObjects.add(obj);
				}
		
			}
			listObjectsToAdd.clear();
	}


	public boolean isInside(Position position){
		return worldI.isInside(position);
	}




	public boolean isFree(int x1, int y1) {
		// TODO Auto-generated method stub
		return worldI.isFree(new Position(x1,y1));
	}


	public boolean collition(WorldObject collitionObject) {
		
		return false;
	}


	public WorldInterface getWorldI() {
		return worldI;
	}


	public void setWorldI(WorldInterface worldI) {
		this.worldI = worldI;
	}


	public ArrayList<WorldObject> getListObjectsToKill() {
		return listObjectsToKill;
	}


	public void setListObjectsToKill(ArrayList<WorldObject> listObjectsToKill) {
		this.listObjectsToKill = listObjectsToKill;
	}


	public ArrayList<WorldObject> getListObjectsToAdd() {
		return listObjectsToAdd;
	}


	public void setListObjectsToAdd(ArrayList<WorldObject> listObjectsToAdd) {
		this.listObjectsToAdd = listObjectsToAdd;
	}


	public int getScreenHeight() {
		// TODO Auto-generated method stub
		return 0;
	}


	public int getScreenWidth() {
		// TODO Auto-generated method stub
		return 0;
	}


	public void generateCollitions() {
		// TODO Auto-generated method stub
		
	}


	public ArrayList<WorldObject> getListObjectsToDesactivate() {
		// TODO Auto-generated method stub
		return listObjectsToDesactivate;
	}


	public WorldObject getObject(int id) {
		
		for(WorldObject obj : listObjects){
			if(obj.getID()==id){
				return new WorldObject(obj);
			}
			WorldObject o = obj.getObject(id);
			if(o!=null){
				return o;
			}
		}
		return null;
	}
	
	private WorldObject getRealObject(int id){
		for(WorldObject obj : listObjects){
			if(obj.getID()==id){
				return obj;
			}
			WorldObject o = obj.getObject(id);
			if(o!=null){
				return o;
			}
		}
		return null;
	}


	public void addLoadObject(WorldObject object) {
		
		listObjects.add(object);
		
		
	}
	
	public void addObject(WorldObject object) {
		object.setActive(true);
		
		listObjectsToAdd.add(new WorldObject(object));
		System.out.println("AGEREGUEUE UN OBJETO UNIVERSES");
		
	}


	public void removeObject(WorldObject object) {
		listObjectsToKill.add(object);
		
	}


	public WorldObject getBackgroundObject() {
		// TODO Auto-generated method stub
		return backgroundObject;
	}


	public void setBackgroundObject(WorldObject object) {
		this.backgroundObject=object;
		
	}


	public String getSize() {
		String msg = getRealHeight()+";"+getRealWidth();
		return msg;
	}


	public ArrayList<WorldObject> getListObjects(int objectReference) {
		ArrayList<WorldObject> list = new ArrayList<WorldObject>();
		
		for(WorldObject obj : listObjects){
			if(obj.getObjectReference()==objectReference){
				list.add(obj);
			}
		}
		
		return list;
		
	}


	public void disconnect(int idCliente) {
		for(WorldObject obj : listObjects){
			if(obj.getOwner()==idCliente){
				listDisconnectObjects.add(obj);
				listObjectsToKill.add(obj);
			}
		}
		//updateDatabaseObject(idCliente);
	}


	private void updateDatabaseObject(int owner) {
		
		Session session = CledaConnector.getInstance().getSession();
	    session.beginTransaction();
		//usa
		List<MWorld> list = new ArrayList<MWorld>();
	    list =session.createCriteria(MWorldObject.class).add(Restrictions.eq("owner", owner)).list();
	    
	    		
	}


	public boolean connect(int idCliente) {
		boolean found=false;
		
		
		
		for(WorldObject obj: listDisconnectObjects){
			if(obj.getOwner()==idCliente){
				listObjectsToAdd.add(obj);
				found=true;
			}
		}
		return found;
	}

	public void setAction(ActionInterface action) {
		WorldObject objectReal = getRealObject(action.getTarget().getID());
		//System.out.println("Agregue la accion "+action.getName()+objectReal.getReal());
		action.setTarget(objectReal);
		objectReal.setImmediatlyActionToDo(action);
	}


	public void desactivateAllHumanInteractions(WorldObject object) {
		WorldObject objectReal = getRealObject(object.getID());
		for(HumanInteraction hu : objectReal.getListHumanInteractions()){
			hu.setActive(false);
		}
	}

	
	public void generateDialogMessage(int clientId, String message) {
		message = Message.DIALOG_TO_SHOW+System.getProperty("line.separator")+message;
		MessageFromClient messageFromClient = new MessageFromClient(clientId, message);
		messageList.add(messageFromClient);
		
	}

	public void generateMessage(int clientId, String message) {
		
		MessageFromClient messageFromClient = new MessageFromClient(clientId, message);
		messageList.add(messageFromClient);
		
	}

	public ArrayList<MessageFromClient> getMessageFromClient(int idClient) {
		
		Object message [] = messageList.toArray();
		ArrayList<MessageFromClient> listToReturn= new ArrayList<MessageFromClient>();
		
		for(int i=0;i<message.length;i++){
			MessageFromClient msg = (MessageFromClient) message[i];
			if(msg.getClientId()==idClient){
				listToReturn.add(new MessageFromClient(msg));
				messageList.remove(msg);
			}
			
		}
		
		return listToReturn;
		
	}


	public void addAgent(AgentInterface agent) {
		listAgent.add(agent);
		
	}


	public int getUniverseId() {
		return universeId;
	}


	public void setUniverseId(int universeId) {
		this.universeId = universeId;
	}


	public Space getSpace() {
		return space;
	}


	public void setSpace(Space space) {
		this.space = space;
	}
	
	
	
	
}
